﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YahooManaged.Finance;

namespace download
{
    class Ana
    {
        private HistQuotesDataChain qd; //最原始数据
        private StockInfo[] sis = null; // qd+一些附加Feature
        private static Random a = new Random(System.DateTime.Now.Millisecond);
        private int count = 0;
        public Ana(HistQuotesDataChain qd)
        {
            this.qd = qd;
            count = qd.Count;
            sis = new StockInfo[count];
            for(int i = 0; i < count; i++)
            {
                StockInfo si = new StockInfo();
                si.data = qd[i];
                si.max22 = GetMax22(i);
                sis[i] = si;
            }
            CalMacd();
        }

        // 长期持有策略
        public double HoldStrategy()
        {
            return qd[count - 1].CloseAdjusted / qd[0].CloseAdjusted - 1;
        }

        // 长期持有策略
        public double HoldStrategy(DateTime from, DateTime to)
        {
            int start = GetIdx(from);
            int end = GetIdx(to);
            if (start < 0 || end < 0) // 日期错误
            {
                Console.WriteLine("datetime error! from=" + from + " to=" + to + " start=" + start + " end=" + end);
                return -1;
            }
            return qd[end].CloseAdjusted / qd[start].CloseAdjusted - 1;
        }

        // 随机策略，返回收益
        public double RandomStrategy()
        {
            DateTime from = qd[0].TradingDate;
            DateTime to = qd[count-1].TradingDate; 
            //默认全部时间
            return RandomStrategy(from, to);
        }
        // 随机策略，返回收益
        public double RandomStrategy(DateTime from, DateTime to)
        {
            int start = GetIdx(from);
            int end = GetIdx(to);
            if (start < 0 || end < 0) // 日期错误
            {
                Console.WriteLine("datetime error! from=" + from + " to=" + to + " start=" + start + " end=" + end);
                return -1;
            }

            double gain = 1;
            int fail = 0;
            int win = 0;

            double buy = 0;
            Boolean have = false;
            for (int i = start+20; i <= end; i++)
            {
                int key = a.Next(100);
                HistQuoteData cur = qd[i];
                if (!have && key < 20)
                {
                    have = true;
                    buy = qd[i].CloseAdjusted;
                    continue;
                }
                if (have && key < 20)
                {
                    have = false;
                    double curGain = qd[i].CloseAdjusted - buy;
                    if (curGain > 0)
                        win++;
                    else
                        fail++;
                    gain *= 1 + curGain / buy;
                }
            }
            gain -= 1;
            Console.WriteLine("gain=" + gain + " fail=" + fail + " win=" + win);
            return gain;
        }

        public double Breakthrough()
        {
            DateTime from = qd[0].TradingDate;
            DateTime to = qd[count - 1].TradingDate;
            //默认全部时间
            return Breakthrough(from, to);
        }
        // 突破新高策略，返回收益
        public double Breakthrough(DateTime from, DateTime to)
        {
            //验证rule1： 如果达到最近1个月高点，且能坚持1天，则后天收盘入，直至第一次收盘跌>1.5%卖出(或者累积跌幅)
            int start = GetIdx(from);
            int end = GetIdx(to);
            if (start < 0 || end < 0) // 日期错误
            {
                Console.WriteLine("datetime error! from=" + from + " to=" + to + " start=" + start + " end=" + end);
                return -1;
            }

            double gain = 1;
            int fail = 0;
            int win = 0;

            double buy = 0;
            Boolean have = false;
            for (int i = start + 20; i <= end; i++)
            {
                StockInfo cur = sis[i];
                if (cur.data.CloseAdjusted >= cur.max22 && qd[i + 1].CloseAdjusted > cur.max22 && !have)
                {
                    //买入
                    buy = sis[i + 2].data.CloseAdjusted;
                    i += 2;
                    have = true;
                    Console.WriteLine(qd[i + 2].TradingDate + " buy@" + buy);
                }
                else if (have && qd[i].CloseAdjusted < qd[i - 1].CloseAdjusted * 0.985)
                {
                    //卖出
                    have = false;
                    double curGain = qd[i + 1].CloseAdjusted - buy;
                    if (curGain > 0)
                        win++;
                    else
                        fail++;
                    gain *= 1 + curGain / buy;
                    i++;
                    Console.WriteLine(qd[i + 1].TradingDate + " sell@" + qd[i + 1].CloseAdjusted);
                }
            }
            gain -= 1;
            Console.WriteLine("gain=" + gain + " fail=" + fail + " win=" + win);
            return gain;
        }

        // macd策略 macd斜率>0买入 <0卖出
        public double MacdStrategy()
        {
            DateTime from = qd[0].TradingDate;
            DateTime to = qd[count - 1].TradingDate;
            //默认全部时间
            return MacdStrategy(from, to);
        }

        // macd策略 macd斜率>0买入 <0卖出
        public double MacdStrategy(DateTime from, DateTime to)
        {
            int start = GetIdx(from);
            int end = GetIdx(to);
            if (start < 0 || end < 0) // 日期错误
            {
                Console.WriteLine("datetime error! from=" + from + " to=" + to + " start=" + start + " end=" + end);
                return -1;
            }

            double gain = 1;
            int fail = 0;
            int win = 0;

            double buy = 0;
            Boolean have = false;
            for (int i = start + 25; i <= end; i++)  // +33确保有macd值，再加1，确保有macd的斜率
            {
                Console.WriteLine(qd[i].TradingDate + " dea=" + sis[i].dea + " macd=" + sis[i].macd);
                if (sis[i].macd > sis[i - 1].macd + 0.1 && sis[i].macd > 0 && !have)
                {
                    have = true;
                    buy = qd[i + 1].Open;
                    i++;
                    Console.WriteLine(qd[i + 1].TradingDate + " buy@" + buy);
                    continue;
                }
                if (sis[i].macd < sis[i - 1].macd - 0.04 && have)
                {
                    have = false;
                    double curGain = qd[i + 1].Open - buy;
                    if (curGain > 0)
                        win++;
                    else
                        fail++;
                    gain *= 1 + curGain / buy;
                    i++;
                    Console.WriteLine(qd[i + 1].TradingDate + " sell@" + qd[i + 1].Open + " cur_gain=" + curGain/buy + " gain=" + gain);
                }
            }
            gain -= 1;
            Console.WriteLine("gain=" + gain + " fail=" + fail + " win=" + win);
            return gain;
        }

        private double GetMax22(int cur)
        {
            double max = 0;
            int start = cur - 22;
            if (start < 0)
                start = 0;
            for (int i = start; i <= cur; i++)
                if (qd[i].CloseAdjusted > max)
                    max = qd[i].CloseAdjusted;
            return max;
        }

        // 找到某时间（年月日）对应的qd下标，二分查找，不在 return -1
        private int GetIdx(DateTime dt)
        {
            int low = 0;
            int high = count - 1;
            while (low <= high)
            {
                int mid = (low + high) / 2;
                if (qd[mid].TradingDate.Equals(dt))
                    return mid;
                if (qd[mid].TradingDate < dt)
                    low = mid + 1;
                else
                    high = mid - 1;
            }
            return -1;
        }

        // 计算Macd
        private void CalMacd2()
        {
            // 计算ema12 >= 11 开始有
            sis[11].ema12 = 0;
            for(int i = 0; i < 12; i++)
                sis[11].ema12 += (i+1) * qd[i].CloseAdjusted;
            sis[11].ema12 /= 78;
            for (int i = 12; i < count; i++)
            {
                sis[i].ema12 = EmaRecursion(qd[i].CloseAdjusted, 12, sis[i - 1].ema12);
            }
            // 计算ema26 >= 25 开始有
            sis[25].ema26 = 0;
            for (int i = 0; i < 26; i++)
                sis[25].ema26 += (i + 1) * qd[i].CloseAdjusted;
            sis[25].ema26 /= 351;
            for (int i = 26; i < count; i++)
            {
                sis[i].ema26 = EmaRecursion(qd[i].CloseAdjusted, 26, sis[i - 1].ema26);
            }
            // 计算dif >= 25开始有
            for (int i = 25; i < count; i++)
            {
                sis[i].dif = sis[i].ema12 - sis[i].ema26;
            }
            // 计算dea：ema(dif,9) >= 33开始有
            sis[33].dea = 0;
            for (int i = 25; i < 34; i++)
                sis[33].dea += (i - 24) * sis[i].dif;
            sis[33].dea /= 45;
            for (int i = 34; i < count; i++)
            {
                sis[i].dea = EmaRecursion(sis[i].dif, 9, sis[i - 1].dea);
            }
            // 计算macd >= 33开始有
            for (int i = 33; i < count; i++)
                sis[i].macd = 2 * (sis[i].dif - sis[i].dea);

        }

        // FIX:
        private void CalMacd()  
        {
            // 计算ema12 >= 11 开始有
            sis[0].ema12 = qd[0].CloseAdjusted;
            for (int i = 1; i < count; i++)
            {
                sis[i].ema12 = EmaRecursion(qd[i].CloseAdjusted, 12, sis[i - 1].ema12);
            }
            // 计算ema26 >= 25 开始有
            sis[0].ema26 = qd[0].CloseAdjusted;
            for (int i = 1; i < count; i++)
            {
                sis[i].ema26 = EmaRecursion(qd[i].CloseAdjusted, 26, sis[i - 1].ema26);
            }
            // 计算dif >= 25开始有
            for (int i = 0; i < count; i++)
            {
                sis[i].dif = sis[i].ema12 - sis[i].ema26;
            }
            // 计算dea：ema(dif,9) >= 33开始有
            sis[0].dea = sis[0].dif;
            for (int i = 1; i < count; i++)
            {
                sis[i].dea = EmaRecursion(sis[i].dif, 9, sis[i - 1].dea);
            }
            // 计算macd >= 33开始有
            for (int i = 25; i < count; i++)
                sis[i].macd = 2 * (sis[i].dif - sis[i].dea);

        }

        // ema递推公式
        private double EmaRecursion(double value, int n, double lastEma)
        {
            return (2 * value + (n - 1) * lastEma) / (n + 1);
        }
    }
}
